use std::ops::{Add, Mul, Neg, Sub};
use num_traits::Num;
use crate::autograd::add_backward::AddBackward;
use crate::autograd::function::{ForwardArgs, Function};
use crate::autograd::mul_backward::MulBackward;
use crate::autograd::neg_backward::NegBackward;
use crate::autograd::sub_backward::SubBackward;
use crate::Tensor;

impl<T> Add for &Tensor<T>
    where
        T: Copy + Num + 'static
{
    type Output = Tensor<T>;

    fn add(self, other: Self) -> Self::Output {
        let args = ForwardArgs::TensorTensor(self, other);
        AddBackward::<T>::apply(args)
    }
}

impl<T> Mul for &Tensor<T>
    where
        T: Copy + Num + 'static
{
    type Output = Tensor<T>;

    fn mul(self, other: Self) -> Self::Output {
        let args = ForwardArgs::TensorTensor(self, other);
        MulBackward::<T>::apply(args)
    }
}

impl<T> Sub for &Tensor<T>
    where
        T: Copy + Num + 'static
{
    type Output = Tensor<T>;

    fn sub(self, other: Self) -> Self::Output {
        let args = ForwardArgs::TensorTensor(self, other);
        SubBackward::<T>::apply(args)
    }
}

impl<T> Neg for &Tensor<T>
    where
        T: Copy + Num + 'static
{
    type Output = Tensor<T>;

    fn neg(self) -> Self::Output {
        let args = ForwardArgs::TensorOther(self, &[]);
        NegBackward::<T>::apply(args)
    }
}
